package com.google.code.p.restrpc.core.registry;

import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.code.p.restrpc.RpcProtocal;
import com.google.code.p.restrpc.RpcRequest;
import com.google.code.p.restrpc.RpcResponse;
import com.google.code.p.restrpc.client.RpcOptionsFactory;
import com.google.code.p.restrpc.client.proxy.RpcProxyFactory;
import com.google.code.p.restrpc.directory.RemoteServiceDefinition;
import com.google.code.p.restrpc.directory.service.DirectoryService;

/**
 * @author dz
 */
public class RegistryClientFactory {
    
    private static final Log logger = LogFactory.getLog(RegistryClientFactory.class);
    
    /**
     * @return
     */
    public static RegistryClient getInstance() {
        return RegistryClientHolder.instance;
    }
    
    private static class RegistryClientHolder {
        static RegistryClient instance = newRegistryClient();
        
        private static RegistryClient newRegistryClient() {
            return new RegistryClientFactory().getInstance(RpcOptionsFactory.getInstance()
                                                                            .getDirectoryServiceUrl());
        }
    }
    
    private static ConcurrentHashMap<String, RegistryClient> registryClients = new ConcurrentHashMap<String, RegistryClient>();
    
    /**
     * @param url
     * @return
     */
    public static RegistryClient getInstance(String url) {
        RegistryClient client = registryClients.get(url);
        if (null != client) {
            return client;
        }
        
        final DirectoryService directoryService = RpcProxyFactory.getInstance()
                                                                 .createProxy(DirectoryService.class,
                                                                              url);
        
        registryClients.putIfAbsent(url, new RegistryClient() {
            
            public boolean isRegistered(String address, int port) {
                return directoryService.isRegistered(address, port);
            }
            
            public void unregisterAll(String address, int port) {
                if (logger.isDebugEnabled()) {
                    logger.debug("unregisterAll " + address + ":" + port);
                }
                directoryService.unregisterAll(address, port);
            }
            
            public void activate(String address, int port) {
                directoryService.activate(address, port);
                // TODO Auto-generated method stub
            }
            
            public String getStatus(String address, int port) {
                // TODO Auto-generated method stub
                return null;
            }
            
            public void registerNode(String address, int port, int weight) {
                // TODO Auto-generated method stub
                
            }
            
            public void registerService(String serviceName,
                                        String serviceUrl,
                                        int weight) {
                if (StringUtils.isEmpty(serviceName)) {
                    throw new IllegalArgumentException("The 'serviceName' can't be null or empty!");
                }
                if (StringUtils.isEmpty(serviceUrl)) {
                    throw new IllegalArgumentException("The 'serviceUrl' can't be null or empty!");
                }
                
                RpcRequest[] rpcRequests = RpcOptionsFactory.getInstance()
                                                            .getUrlParser()
                                                            .parse(serviceUrl);
                for (RpcRequest rpcRequest : rpcRequests) {
                    RemoteServiceDefinition sd = new RemoteServiceDefinition();
                    sd.setRemoteAddress(rpcRequest.getAddress());
                    sd.setRemotePort(rpcRequest.getPort());
                    sd.setContextPath(rpcRequest.getContext());
                    sd.setServiceName(serviceName);
                    sd.setWeight(weight);
                    if (rpcRequest.getProtocal() == RpcProtocal.HTTPS) {
                        sd.setTlsEnabled(true);
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("registerService " + sd);
                    }
                    directoryService.register(sd);
                }
            }
            
            public void unregisterService(String serviceName, String serviceUrl) {
                if (StringUtils.isEmpty(serviceName)) {
                    throw new IllegalArgumentException("The 'serviceName' can't be null or empty!");
                }
                if (StringUtils.isEmpty(serviceUrl)) {
                    throw new IllegalArgumentException("The 'serviceUrl' can't be null or empty!");
                }
                
                RpcRequest[] rpcRequests = RpcOptionsFactory.getInstance()
                                                            .getUrlParser()
                                                            .parse(serviceUrl);
                for (RpcRequest rpcRequest : rpcRequests) {
                    RemoteServiceDefinition sd = new RemoteServiceDefinition();
                    sd.setRemoteAddress(rpcRequest.getAddress());
                    sd.setRemotePort(rpcRequest.getPort());
                    sd.setContextPath(rpcRequest.getContext());
                    sd.setServiceName(serviceName);
                    if (logger.isDebugEnabled()) {
                        logger.debug("unregisterService " + sd);
                    }
                    directoryService.unregister(sd);
                }
            }
            
        });
        
        return registryClients.get(url);
    }
    
    private RegistryClientFactory() {
    }
    
}
